#pragma once
#include "socket_server.h"
#include "connection.h"
#include "epoll_server.h"
#include "protocol.h"
#include "calculater.h"
#include "threadpool.h"

// 每个连接对应的回调函数方法的实现
const static int g_num = 1024;
class ConnectFunc
{
public:
    ConnectFunc() : _tr()
    {
    }

    // 处理listen套接字
    // 1.1读事件就绪条件-建立新连接
    void AcceptConnect(Connection *pcon)
    {
        // 该listen套接字被设置为et模式，所以需要同时accept所有此刻到达的连接

        while (true)
        {
            sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int connect_sockfd = accept(pcon->Getfd(), (sockaddr *)&peer, &len);
            if (connect_sockfd < 0) // accept失败
            {
                if (errno == EWOULDBLOCK) // EAGAIN
                {
                    cout << "已经accept所有的连接请求..." << endl;
                    break;
                }
                else if (errno == EINTR)
                {
                    cout << "收到信号导致读取中断..." << endl;
                    continue;
                }
                else
                {
                    cerr << "get connect erro" << endl;
                    break;
                }
            }
            else
            {
                cout << "get a newsockfd = " << connect_sockfd << endl;

                // 将新连接设置非阻塞，同时封装成connection 交由epoll_server进行管理
                SetNoBlock(connect_sockfd);

                func_c recver = std::bind(&ConnectFunc::Recver, this, placeholders::_1);
                func_c sender = std::bind(&ConnectFunc::Sender, this, placeholders::_1);
                func_c excepter = std::bind(&ConnectFunc::Excepter, this, placeholders::_1);

                Connection *newpcon = Connection_factor::BuildNormalConnection(
                    connect_sockfd, EPOLLIN | EPOLLET,
                    recver, sender, excepter,
                    pcon->_R);

                pcon->_R->AddConnection(newpcon);
            }
        }
    }

    // IO处理
    // 1.2读事件就绪的条件-接收缓冲区有数据可读
    void Recver(Connection *pcon)
    {
        while (true)
        {
            char buffer[g_num]; // 每次recv都是从起始位置开始写入
            // 该文件描述符已经设置为非阻塞状态
            ssize_t n = recv(pcon->Getfd(), buffer, sizeof(buffer) - 1, 0);

            if (n > 0)
            {
                buffer[n] = 0;
                pcon->AddInBuffer(buffer);
            }

            else if (n == 0) // 连接已关闭
            {
                pcon->_excepter(pcon);
                return;
            }

            else // 返回值为-1：1.数据没有准备好 2.真的读取出错
            {
                if (errno == EWOULDBLOCK) // EAGAIN
                {
                    cout << "数据全部读完..." << endl;
                    break;
                }
                else if (errno == EINTR)
                {
                    cout << "收到信号导致读取中断..." << endl;
                }
                else
                {
                    cerr << "读取出错, 异常处理" << endl;
                    pcon->_excepter(pcon);
                    return;
                    // pcon->_R->CloseConnection(pcon->Getfd());
                    // close(pcon->Getfd());
                }
            }
        }

        // ***数据读取结束，协议化处理
        HandlerRequest(pcon);
    }

    // 2.1写事件就绪-内核发送缓冲区有空间
    void Sender(Connection *pcon)
    {
        // 正常情况下发送缓冲区都不会被写满，所以数据可以立马被send到内核的发送缓冲区
        // 只有当发送缓冲区的数据写满了，才不能继续发送数据，而是交给epoll处理
        // 判断读事件是否就绪(设置epoll关心)，如果就绪才可以继续发送数据

        string &outbuffer = pcon->GetOutbuffer(); // 引用拷贝
        while (true)
        {
            ssize_t n = send(pcon->Getfd(), outbuffer.c_str(), outbuffer.size(), 0);
            if (n > 0) // n个字节数据成功发送出去
            {
                outbuffer.erase(0, n); // 移出
                if (outbuffer.empty()) // 全部发送出去
                    return;
            }
            else
            {
                if (errno == EAGAIN)
                    break; // 数据未能发送出去，即发送缓冲区已满
                else if (errno == EINTR)
                    continue; // 信号中断了
                else
                {
                    Excepter(pcon); // 异常处理
                    return;
                }
            }
        }
        if (!pcon->OutbufferIsempty()) // 还有数据未send完
        {
            // EAGAIN发送缓冲区满了
            // 设置epoll关心EPOLLOUT事件
            pcon->_R->ModReadWrite(pcon->Getfd(), true, true);
        }
        else // 都发完了，取消EPOLLOUT关心
        {
            pcon->_R->ModReadWrite(pcon->Getfd(), true, false);
        }
    }

    void Excepter(Connection *pcon)
    {
        // 1.取消epoll关心,并取消对该connection对象的管理
        pcon->_R->CloseConnection(pcon->Getfd());

        // 2.关闭文件描述符
        pcon->Close();
        cout << pcon->Getfd() << "号文件描述符连接已断开..." << endl;

        // 3.释放该对象
        delete pcon;
    }

    ~ConnectFunc()
    {
    }

private:
    threadpool<calculater::Cal, shared_ptr<Response>> _tr; // 线程池

private:
    //***协议化字段处理
    void HandlerRequest(Connection *pcon)
    {
        string message;
        while (protocol::Decode(pcon->GetInbuffer(), message)) // 循环解包 解决粘包问题
        {
            // message是一个完整的报文

            // 1.构建请求 进行反序列化(将请求报文的数据存到reqs里)
            shared_ptr<Request> reqs = protocol::Factor::BuildRequest();
            reqs->Deserialize(message);

            // 2构建cal对象将reqs请求进行计算处理,同时返回响应
            calculater::Cal cal(reqs);

            // 3.交给线程池进行执行处理计算
            _tr.Push(cal);
            // 等处理结束再拿数据
            while (!_tr.IsData())
            {
                ;
            }
            shared_ptr<Response> resp = _tr.Front();
            resp->Info(); // 打印计算处理的结果

            // 3.将响应进行序列化+添加报头
            string resp_str;
            resp->Serialize(resp_str);
            protocol::Encode(resp_str);

            // 4.将打包好的应答全部追加到输出缓冲区
            pcon->AddOutBuffer(resp_str);
        }
        // ***处理发送问题
        if (!pcon->OutbufferIsempty())
            Sender(pcon);
    }
};